/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.component.math;

import robocup.component.SConf;
import robocup.component.geometry.Vektor;

/**
 * Robocup math helper methods.
 */
public class RCMath {

	/**
	 * calculates a geometric row. Used for movement calculations.
	 * 
	 * @param q - value (decay)
	 * @param n - time (cycles)
	 * @return (1-q^n)/(1-q)
	 */
	public static double calculateRow(double q, double n) {
		if (q == 1)
			return n;

		return (1 - Math.pow(q, n)) / (1 - q);
	}

	/**
	 * returns the given angle normalized between 0..360
	 * 
	 * @param angle
	 *            in degree
	 * @return the given angle normalized between 0..360
	 */
	public static double getPositiveNormalizedAngle(double angle) {
		double a = Vektor.normalize(angle);
		if (a < 0)
			return a + 360.0;
		else
			return a;
	}

	/**
	 * the cycles a ball needs to reach a given distance.
	 * 
	 * @param initialBallSpeed - the initial ball's speed
	 * @param distance - the distance for the ball to travel
	 * @return Integer.MAX_VALUE if not possible, the needed cycles otherwise
	 */
	public static int getCyclesForBallReachingDistance(double initialBallSpeed,
			double distance) {
		if (distance == 0)
			return 0;

		double term1 = 1.0 - (distance / initialBallSpeed * (1.0 - SConf
				.getInstance().BALL_DECAY));
		if (term1 <= 0) {
			return Integer.MAX_VALUE;
		} else {
			return 1 + (int) (Math.log(term1) / Math
					.log(SConf.getInstance().BALL_DECAY));
		}
	}

	/**
	 * This method calculates the gaussian value at a given point for given
	 * variance and middle!
	 * 
	 * @param val -
	 *            the value for which the gauss-value will be calculated
	 * @param med -
	 *            the middle
	 * @param var -
	 *            the variance
	 * @return - calculation of the gauss-function!
	 */
	public static double gauss(double val, double med, double var) {

		double gauss = (1.0 / (Math.sqrt(2 * Math.PI) * var))
				* Math.exp(-(Math.pow(val - med, 2) / 2 * Math.pow(var, 2)));
		return gauss;
	}

	/**
	 * tells if an angle is between to other angles (all angles in degree).
	 * 
	 * @param angle -
	 *            the angle
	 * @param leftAngle -
	 *            the left limit [-180;180]
	 * @param rightAngle -
	 *            the right limit [-180;180]
	 * @return true if the angle is between the limitint angles
	 */
	public static boolean isBetween(double angle, double leftAngle,
			double rightAngle) {
		if (leftAngle < rightAngle) {
			return angle >= leftAngle && angle <= rightAngle;
		} else {
			rightAngle += 360;
			angle += angle < 0 ? 360 : 0;
			return angle >= leftAngle && angle <= rightAngle;
		}
	}

	/**
	 * tells if an angle is between to positions.
	 * 
	 * @param angle -
	 *            the angle
	 * @param basePos -
	 *            the base position
	 * @param leftPos -
	 *            the left limiting position
	 * @param rightPos -
	 *            the right limiting
	 * @return true if the angle is in the triangle of the three vektors seen
	 *         from the base position.
	 */
	public static boolean isBetween(double angle, Vektor basePos,
			Vektor leftPos, Vektor rightPos) {
		return isBetween(angle, basePos.getAngleTo(leftPos), basePos
				.getAngleTo(rightPos));
	}

	/**
	 * This method rounds a number to the number of decimal places given in
	 * <code>PARAMETER_PRECISION</code>.
	 * 
	 * @param number
	 *            The number to round.
	 * @return The rounded number.
	 */
	public static double round(double number, int precision) {
		double x = Math.pow(10.0, precision);
		return Math.rint(number * x) / x;
	}
  
  /**
     * rounds a double value to 2 digits.
     * 
     * @param d - double value
     * @return rounded double value
   */
  public static double round2(double d) {
    return round(d, 2);
  }
}
